package org.jivesoftware.openfire.redischat;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;

import org.dom4j.Element;
import org.jivesoftware.openfire.PacketException;
import org.jivesoftware.openfire.PacketRouter;
import org.jivesoftware.openfire.RoutingTable;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.muc.CannotBeInvitedException;
import org.jivesoftware.openfire.muc.ConflictException;
import org.jivesoftware.openfire.muc.ForbiddenException;
import org.jivesoftware.openfire.muc.NotAllowedException;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.PacketError.Condition;
import org.xmpp.packet.Presence;
/**
 * 
 * 群组聊天组件服务，一个组件服务可以维护多个房间<br> 
 *
 * 
 */
public class RedisMultiUserChatServiceImpl implements RedisMultiUserChatService {

	private static final Logger Log = LoggerFactory
			.getLogger(RedisMultiUserChatServiceImpl.class);
	private final String chatServiceName;
	private String chatDescription;
	private boolean isHidden = true;
	private boolean serviceEnabled = true;

	public boolean isServiceEnabled() {
		return serviceEnabled;
	}

	public void setServiceEnabled(boolean serviceEnabled) {
		this.serviceEnabled = serviceEnabled;
	}

	/**
	 * 讨论组Cache
	 */
	private Cache<String, RedisMUCRoom> allRoomsCache;
	/**
	 * 用户-讨论组 Relation Cache
	 */
	private Cache<String, RedisMUCUser> allUsersCache;
	private RoutingTable routingTable;
	private PacketRouter router;

	public RedisMultiUserChatServiceImpl(String subdomain, String description,
			Boolean isHidden) {
		new JID(null, subdomain + "."
				+ XMPPServer.getInstance().getServerInfo().getXMPPDomain(),
				null);

		this.chatServiceName = subdomain;
		if (description != null && description.trim().length() > 0) {
			this.chatDescription = description.trim();
		} else {
			this.chatDescription = LocaleUtils
					.getLocalizedString("muc.service-name");
		}
		this.isHidden = isHidden;
	}

	public String getName() {
		return "MultiUserChat";
	}

	public String getDescription() {
		 
		return "MultiUserChat Service";
	}

	public void processPacket(Packet packet) {

		if (!isServiceEnabled()) {
			return;
		}

		try {
			process(packet);
		} catch (PacketException e) {
			Log.error(e.getMessage(), e);
		} catch (UnauthorizedException e) {
			Log.error(e.getMessage(), e);
		}
	}

	public void process(Packet packet) throws UnauthorizedException, PacketException {
		
		if (packet instanceof IQ) {
			process((IQ) packet);
		} else if (packet instanceof Message) {
			process((Message) packet);
		} else if (packet instanceof Presence) {
			process((Presence) packet);
		}
	}

	/**
	 * 
	 * 处理修改主题Message、群组消息Message、邀请Message
	 * add comment by buya
	 *
	 * @param packet
	 */
	public void process(Message packet) {
		
		//filter unsupported messages
		if (Message.Type.error == packet.getType()) {
			Log.warn(LocaleUtils.getLocalizedString("muc.error.not-supported")
					+ " " + packet.toString());
			return;
		}		
		
//		if (recipient.getNode() == null || recipient.getNode().trim().length() == 0) { 
//			Log.warn(LocaleUtils.getLocalizedString("muc.error.not-supported")
//					+ " " + packet.toString());
//			return;
//		} 
		
		JID recipient = packet.getTo();
		JID from = packet.getFrom();
		String roomId = recipient.toBareJID();
		
		try {
			RedisMUCRoom room = allRoomsCache.get(roomId);
			if (room != null && room.hasMember(from)) {
				if (packet.getSubject() != null
						&& packet.getSubject().trim().length() > 0
						&& Message.Type.groupchat == packet.getType()
						&& (packet.getBody() == null || packet.getBody().trim()
								.length() == 0)) {
					// 修改讨论组主题
					changeSubject(packet, room);
				} else {
					Message.Type type = packet.getType();
					String resource = packet.getTo().getResource();

					if (resource != null) {
						resource = resource.trim();
						if (resource.length() == 0) {
							resource = null;
						}
					}

					if (resource == null) {
						if (Message.Type.groupchat == type) {
							// 发送群聊消息
							sendPublicMessage(packet, room);
						} else if (Message.Type.normal == type) {
							// 邀请消息
							procInvitation(packet, room);
						} else {
							sendErrorPacket(packet,
									PacketError.Condition.bad_request);
						}
					} else {
						sendErrorPacket(packet,
								PacketError.Condition.bad_request);
					}

				}
			} else {
				sendErrorPacket(packet,
						PacketError.Condition.recipient_unavailable);
			}
		} catch (Exception e) {
			Log.error(e.getMessage(), e);
		}
		
	}
	
	/**
	 * 处理邀请消息
	 * @param packet
	 * @param room
	 */
	private void procInvitation(Message packet, RedisMUCRoom room) {


		Element userInfo = packet.getChildElement("x",
				"http://jabber.org/protocol/muc#user");
		if (userInfo != null && userInfo.element("invite") != null) {
		    //Message 邀请消息
			@SuppressWarnings("unchecked")
			List<Element> extensions = new ArrayList<Element>(
					packet.getElement().elements());
			extensions.remove(userInfo);
			
			JID from = packet.getFrom();

			@SuppressWarnings("unchecked")
			Iterator<Element> it = userInfo.elementIterator("invite");
			if (reserveRoomSeats(room, userInfo.elements("invite").size())) {//预占座位
				
				String reason = null;
				List<JID> notiyArray = new ArrayList<JID>();
				
				//房间上锁，房间增加成员
				Lock lock = CacheFactory.getLock(room.getJid().toBareJID(), allRoomsCache);
				lock.lock();
				try {
					// 取最新房间数据
					room = allRoomsCache.get(room.getJid().toBareJID());
					while (it.hasNext()) {
						Element info = it.next();
						JID jid = new JID(info.attributeValue("to"));

						boolean added = addMember(room, jid);
						
						if (added) {							
							notiyArray.add(jid.asBareJID());
							if(Log.isDebugEnabled()){
								Log.debug(jid.asBareJID()+ "invited to Room:"+room.getJid());
							}
						}else{
							// 如果添加失败,释放座位
							this.allRoomsCache.decrBy(
									room.getJid().toBareJID(), 1);
						}
						
						if (reason == null || reason.isEmpty())
							reason = info.elementTextTrim("reason");
					}
				} finally {
					lock.unlock();
				}
				
				sendInvitation(packet.getFrom(), room.getJid(), reason, notiyArray);
				ArrayList excludeList =  (ArrayList)((ArrayList)notiyArray).clone();
				excludeList.add(from.asBareJID());
				for(JID jid : notiyArray){
					Presence joinPresence = new Presence();
			        Element childEl = joinPresence.addChildElement("x", "http://jabber.org/protocol/muc#user");
			        Element item = childEl.addElement("item");
			        item.addAttribute("jid", jid.toBareJID());
			        item.addAttribute("affiliation", "member");
			        item.addAttribute("role", "participant");
		        	joinPresence.setFrom( room.getJid().toBareJID()+"/"+jid.getNode());
		        	joinPresence.getElement().addNamespace("", "jabber:client");
		        	broadcast(joinPresence, room, excludeList);
				}
				//回成功
				sendInviteResponse(from,packet.getID(),room.getJid());
			} else {
				sendErrorPacket(packet, PacketError.Condition.bad_request);
			}
			
		} 
		
	}

	/**
	 * 通知邀请者邀请成功
	 * @param from
	 * @param string 
	 */
	private void sendInviteResponse(JID from, String id, JID roomjid) {
		  Message message = new Message();
	        message.setType(Message.Type.groupchat);
	        message.setID(id);
	        message.setTo(from);
	        message.setFrom(roomjid+"/"+from.getNode());
	        Element frag = message.addChildElement("x", "http://jabber.org/protocol/muc#user");
	        frag.addElement("status").addAttribute("code", "100");
	        router.route(message);
	}

	private boolean addMember(RedisMUCRoom room, JID memJid) {
		
		if (room.hasMember(memJid))
		{
			return false;
		}
		 
		room.addMemeber(memJid.asBareJID(), 1);
		//锁定用户
		Lock lock = CacheFactory.getLock(memJid.toBareJID(), allUsersCache);
		lock.lock();
		try {
			RedisMUCUser user = allUsersCache.get(memJid.toBareJID());
			if (user == null) {
				user = new RedisMUCUser(memJid);
			}

			user.joinRoom(room.getJid());
			allUsersCache.put(memJid.toBareJID(), user);
		} finally {
			lock.unlock();
		}
		
		allRoomsCache.put(room.getJid().toBareJID(), room);
		return true;
	}

	/**
	 * 发送邀请消息至被邀请者
	 * 
	 * @param actorJID   	邀请发起人JID
	 * @param roomJID    	讨论组JID
	 * @param reason     	邀请描述
	 * @param notiyArray   	被邀请者列表
	 */
	private void sendInvitation(JID actorJID, JID roomJID, String reason,
			List<JID> notiyArray) {

		Message inviteMessage = new Message();
		inviteMessage.setFrom(roomJID);
		Element xElement = inviteMessage.addChildElement("x",
				"http://jabber.org/protocol/muc#user");
		Element invite = xElement.addElement("invite");
		invite.addAttribute("from", actorJID.toBareJID());
		invite.addElement("reason").addText(reason);

		// 发送邀请至被邀请者
		for (JID jid : notiyArray) {
			inviteMessage.setTo(jid);
			router.route(inviteMessage);
		}
	}
	
	/**
	 * 预占房间座位
	 * 
	 * @param room
	 * @param delta
	 * @return
	 */
	private boolean reserveRoomSeats(RedisMUCRoom room, long delta) {
		String key = room.getJid().toBareJID();
		long currentMember = allRoomsCache.incrBy(key, delta);
		if (currentMember > room.getMaxUsers())
		{
			allRoomsCache.decrBy(key, delta);
			return false;
		}
		return true;
	}

	/**
	 * 发送群聊消息
	 * 
	 * @param packet
	 * @param room
	 */
	private void sendPublicMessage(Message packet, RedisMUCRoom room) {
		packet.setFrom(room.getJid() + "/" + packet.getFrom().getNode());
		send(packet, room);
	}
	
	/**
	 * 发送消息
	 * 
	 * @param packet
	 * @param room
	 */
	public void send(Packet packet, RedisMUCRoom room) {
		if (packet instanceof Message) {
			broadcast((Message) packet, room);
		} else if (packet instanceof Presence) {
			broadcast((Presence) packet, room);
		} else if (packet instanceof IQ) {
			IQ reply = IQ.createResultIQ((IQ) packet);
			reply.setChildElement(((IQ) packet).getChildElement());
			reply.setError(PacketError.Condition.bad_request);
			router.route(reply);
		}
	}
	
	/**
	 * 变更讨论组组题
	 * 
	 * @param packet	
	 * @param room		需变更的讨论组
	 * @throws ForbiddenException
	 */
	private void changeSubject(Message packet, RedisMUCRoom room)
			throws ForbiddenException {
		JID from = packet.getFrom();
		if (room.isCanOccupantsChangeSubject() || room.getOwner().equals(from)) {
			if (packet.getSubject() == null || packet.getSubject().isEmpty() || packet.getSubject().equals(room.getSubject())) {
				return;
			}
			room.setSubject(packet.getSubject());

			// 更新缓存
			String roomId =  room.getJid().toBareJID();
			Lock lock = CacheFactory.getLock(roomId, allRoomsCache);
			lock.lock(); //确保只有一个线程在更新该讨论组
			try{
				allRoomsCache.put(roomId, room);
			}finally{
				lock.unlock();
			}
			
			Message replyMsg = packet.createCopy();
			
			replyMsg.addChildElement("x", "jabber:x:data").addAttribute("type", "modifygroupsubject");
			replyMsg.setFrom(room.getJid() + "/" + packet.getFrom().getNode());
			
			send(replyMsg, room);
		} else {
			throw new ForbiddenException();
		}
	}
	
	/**
	 * 消息包广播
	 * 
	 * @param packet
	 * @param room
	 */
	public void broadcast(Packet packet, RedisMUCRoom room) {
		// roomHistory.addMessage(message);
		RedisMUCMember[] members = room.getMembers();
		//packet.setFrom(room.getJid().asBareJID() + "/" + packet.getFrom().getNode());
		for (RedisMUCMember m : members) {//
			packet.setTo(m.getJid());
			router.route(packet);
		}

		// message.setFrom(from)
	}
	
	
	/**
	 * 消息广播,排除某些成员
	 * 
	 * @param packet
	 * @param room
	 */
	public void broadcast(Packet packet, RedisMUCRoom room, List<JID> execludeList) {
		
		if(execludeList==null){
			broadcast(packet, room);
			
		}else{
		
			RedisMUCMember[] members = room.getMembers();
			
			for (RedisMUCMember m : members) {//
				if(!execludeList.contains(m.getJid().asBareJID())){
					packet.setTo(m.getJid());
					router.route(packet);
				}
			}
		}
 
	}
	
	/**
	 * IQ请求消息处理、
	 * 查询群组成员，查询用户房间
	 * 房主踢人，自己退出
	 * @param packet
	 */
	public void process(IQ packet) {
		if (IQ.Type.error == packet.getType()
				|| IQ.Type.result == packet.getType()) {
			Log.warn(LocaleUtils.getLocalizedString("muc.error.not-supported")
					+ " " + packet.toString());
			return;
		}
		
		
				
		try {
			String namespace = null;		
			IQ replyIQ = IQ.createResultIQ(packet);
			Element queryElement = packet.getChildElement();
			
			if ( queryElement != null && "query".equals(queryElement.getName())) {
				namespace = queryElement.getNamespaceURI();
			}else{
				throw new  ForbiddenException();
			}
			
			if ("jabber:iq:load:rooms".equals(namespace)) {// 查询所在房间列表				
				queryUserRooms(replyIQ, packet);
			} else {
				String roomId = packet.getTo().toBareJID();
				RedisMUCRoom room = allRoomsCache.get(roomId);
				
				if(room == null){
					throw new ForbiddenException();  //不存在该讨论组
				}
				
				if (!room.hasMember(packet.getFrom())) {					
					throw new UnauthorizedException(); // 不是讨论组成员 
				}
				
				if ("jabber:iq:load:member".equals(namespace)) {// 查询房间内成员列表
					queryRoomMembers(replyIQ, packet, room); 
				}else if ("http://jabber.org/protocol/muc#admin".equals(namespace)) {// 成员减少
					kickRoomMember(packet, roomId);
				}else if ("http://jabber.org/protocol/muc#userSetting".equals(namespace)) {// 成员减少
					setRoomMemberInfo(replyIQ,packet, roomId);
				}
				
			} 
			
			router.route(replyIQ);
		} catch (ForbiddenException e) {
			sendErrorPacket(packet, PacketError.Condition.forbidden);
		} catch (UnauthorizedException e) {
			sendErrorPacket(packet, Condition.not_authorized);
		} catch (Exception e) {
			sendErrorPacket(packet, PacketError.Condition.internal_server_error);
			Log.error(e.getMessage(), e);
		}
		
	}
	
	/**
	 * 设置房间成员个性设置
	 * @param replyIQ
	 * @param packet
	 * @param packet 
	 * @param roomId
	 * @throws ForbiddenException
	 * @throws UnauthorizedException 
	 */
	private void setRoomMemberInfo(IQ replyIQ, IQ packet, String roomId) throws ForbiddenException, UnauthorizedException {
		Element element = packet.getChildElement();
		replyIQ.setChildElement(element.createCopy());
		Element item = element.element("item");
		if (item != null) {
			String roomUser = item.attributeValue("jid");
			String newAPNSMsg = item.attributeValue("newAPNSMsg");
			String top = item.attributeValue("top");
			String saveAsRoster = item.attributeValue("saveAsRoster");
			String nickName = item.attributeValue("nickName");
			JID memJid = new JID(roomUser);
			boolean notifyNickName = false;
			RedisMUCRoom room = null;
			// 在操作之前, 即时获取房间的缓存
			Lock roomLock = CacheFactory.getLock(roomId, allRoomsCache);
			roomLock.lock(); // 确保此时只有一个线程在变更该房间
			try {
				room = allRoomsCache.get(roomId);

				if (room == null) {
					// 该房间已经被其他人销毁
					throw new ForbiddenException();
				}
				RedisMUCMember m = room.getMember(memJid);
				if(m!=null){
					m.setNewAPNSMsg(newAPNSMsg);
					m.setSaveAsRoster(saveAsRoster);
					m.setTop(top);
					if(nickName!=null && !nickName.equals(m.getNickName())){
						notifyNickName = true;
						m.setNickName(nickName);
					}
					allRoomsCache.put(roomId, room);
				}else{
					//用户已不在房间中
					throw new UnauthorizedException();
				}
				
			} finally {
				roomLock.unlock();
			}
			//用户在房间的备注名改了，广播通知
			if(notifyNickName && null!=room){
				IQ changeNickName = new IQ();
				changeNickName.setType(Type.set);
				changeNickName.setFrom(roomId);
				Element xElement = changeNickName.setChildElement("query", "http://jabber.org/protocol/muc#setUserNickName");
				xElement.addElement("item")
				.addAttribute("jid",memJid.toBareJID())
				.addAttribute("nickName",nickName);
				
				List<JID> execludeList = new ArrayList<JID>();
				execludeList.add(memJid);
				broadcast(changeNickName, room, execludeList );
			}
		}
	}

	/**
	 * 
	 * Presence 创建房间、加入房间
	 * add comment by buya
	 *
	 * @param packet
	 */
	public void process(Presence packet) {
		if (Presence.Type.error == packet.getType()) {
			return;
		}
		JID recipient = packet.getTo();
		String roomKey = recipient.toBareJID();
		String roomName = recipient.getNode();

		if (roomKey != null) {
			if (packet.getType() != Presence.Type.unavailable) {
				if (recipient.getResource() != null
						&& recipient.getResource().trim().length() > 0) {
					if (packet.isAvailable()) {
						RedisMUCRoom room = allRoomsCache.get(roomKey);
						if (room == null) {
							// 房间管理员创建房间
							JID from = packet.getFrom().asBareJID();
							room = new RedisMUCRoom(roomName, chatServiceName);
							room.setName(recipient.getNode());
							room.setOwner(from);
							room.setJid(recipient.asBareJID());

							// 管理员作为成员加入房间
							if (reserveRoomSeats(room, 1)) {
								addMember(room, from);
							} else {
								sendErrorPacket(packet,
										Condition.internal_server_error);
								return;
							}

							Presence replyPresence = new Presence();
							Element xElement = replyPresence.addChildElement(
									"x", "http://jabber.org/protocol/muc#user");
							xElement.addElement("item")
									.addAttribute("affiliation", "owner")
									.addAttribute("role", "moderator")
									.addAttribute("jid",
											packet.getFrom().toBareJID())
									.addAttribute("nick",
											packet.getFrom().getNode());

							xElement.addElement("status").addAttribute("code",
									"201");

							replyPresence.setTo(packet.getFrom());
							replyPresence.setFrom(room.getJid());
							router.route(replyPresence);

						}
					}
				}
			}
		}
	}
	
	/**
	 * 校验是否存在该讨论组里面,
	 * 存在返回 		true
	 * 不存在返回	false
	 * 
	 * @param room
	 * @param fromJID
	 * @return
	 */
//	private boolean needEntryRoom(RedisMUCRoom room, JID fromJID) {
//		
//		return checkExist(fromJID, room.getMembers());
//	}
	
//	/**
//	 * 检查fromJID, 是否存在列表中
//	 * 存在返回 		true
//	 * 不存在返回	false;
//	 * 
//	 * @param fromJID
//	 * @param list
//	 * @return
//	 */
//	private boolean checkExist(JID fromJID, List<RedisMUCMember> list) {
//		if (list == null || list.isEmpty())
//			return false;
//		
//		for (RedisMUCMember member : list)
//		{
//			if (member.getJid().asBareJID().equals(fromJID.asBareJID()))
//			{
//				return true;
//			}
//		}
//		
//		return false;
//	}

	public void initialize(JID paramJID, ComponentManager paramComponentManager)
			throws ComponentException {
		XMPPServer server = XMPPServer.getInstance();
		serviceEnabled = true;

		allRoomsCache = CacheFactory.createCache("allRoomsCache");
		allUsersCache = CacheFactory.createCache("allUsersCache");
		routingTable = server.getRoutingTable();
		router = server.getPacketRouter();
	}

	public void start() {

		ArrayList<String> params = new ArrayList<String>();
		params.clear();
		params.add(getServiceDomain());

		Log.info(LocaleUtils.getLocalizedString("startup.starting.Redis.muc",
				params));
		/*
		 * for (LocalMUCRoom room : MUCPersistenceManager.loadRoomsFromDB(this,
		 * this.getCleanupDate(), router)) {
		 * rooms.put(room.getName().toLowerCase(), room); }
		 */
	}

	public void stop() {
		XMPPServer.getInstance().getIQDiscoInfoHandler()
				.removeServerNodeInfoProvider(this.getServiceDomain());
		XMPPServer.getInstance().getServerItemsProviders().remove(this);
		routingTable.removeComponentRoute(getAddress());

	}

	public void shutdown() {
		// TODO Auto-generated method stub

	}

	public String getServiceDomain() {
		return chatServiceName + "."
				+ XMPPServer.getInstance().getServerInfo().getXMPPDomain();
	}

	public JID getAddress() {
		return new JID(null, getServiceDomain(), null, true);
	}

	public String getServiceName() {
		return chatServiceName;
	}

	private void sendErrorPacket(Packet packet, PacketError.Condition error) {
		if (packet instanceof IQ) {
			IQ reply = IQ.createResultIQ((IQ) packet);
			reply.setChildElement(((IQ) packet).getChildElement().createCopy());
			reply.setError(error);
			router.route(reply);
		} else {
			Packet reply = packet.createCopy();
			reply.setError(error);
			reply.setFrom(packet.getTo());
			reply.setTo(packet.getFrom());
			router.route(reply);
		}
	}
	

	/**
	 * 
	 * 踢人/退出
	 *
	 * @param reply
	 * @param packet
	 * @param room
	 * @param allUsersCache
	 * @param allRoomsCache
	 */
	private void kickRoomMember(IQ packet, String roomId) throws UnauthorizedException, ForbiddenException {
		Element element = packet.getChildElement();
		JID recipient = packet.getTo();
		Element item = element.element("item");
		if (item != null) {
			String removeUser = item.attributeValue("jid");
			JID removeJID = new JID(removeUser);
			JID from = packet.getFrom();
			String affiliation = item.attributeValue("role");

			// 在操作之前, 即时获取房间的缓存
			Lock roomLock = CacheFactory.getLock(roomId, allRoomsCache);
			roomLock.lock(); // 确保此时只有一个线程在变更该房间
			try {
				RedisMUCRoom room = allRoomsCache.get(roomId);

				if (room == null) {
					// 该房间已经被其他人销毁
					throw new ForbiddenException();
				}

				if ((room.getOwner().getNode().equals(from.getNode()) // 房主踢人
						|| from.getNode().equals(removeJID.getNode())) // 自己退出
						&& "none".equals(affiliation)) {

					Lock userLock = CacheFactory.getLock(removeJID.toBareJID(),
							allUsersCache);
					userLock.lock(); // 确保此时只有一个线程在变更该用户
					try {
						RedisMUCUser user = allUsersCache.get(removeJID
								.toBareJID());

						if (null != user) {
							room.kickMemeber(user.getJid());
							user.leaveRoom(room.getJid());
							allRoomsCache.decrBy(room.getJid().toBareJID(), 1);

							if (room.getMembers().length == 0) {
								// 销毁房间
								allRoomsCache.remove(recipient.toBareJID());
							} else {
								allRoomsCache.put(recipient.toBareJID(), room);
							}

							if (user.getRoomJids().length == 0) {
								allUsersCache.remove(removeJID.toBareJID());
							} else {
								allUsersCache.put(removeJID.toBareJID(), user);
							}
						} else {
							// 该用户已经退出了所有讨论组
							throw new ForbiddenException();
						}
					} finally {
						userLock.unlock();
					}
					Presence kickPresence = new Presence(
							Presence.Type.unavailable);
					kickPresence.setFrom(packet.getFrom());
					Element xElement = kickPresence.addChildElement("x",
							"http://jabber.org/protocol/muc#user");
					Element xitem = xElement.addElement("item");
					
					xitem.addAttribute("affiliation", "none")
							.addAttribute("jid", removeJID.toBareJID())
							.addAttribute("role", "none"); 

					xElement.addElement("status").addAttribute("code", "307");
					kickPresence.setFrom(room.getJid() + "/"
							+ from.getNode());
					xitem.addElement("actor").addAttribute("jid", from.toBareJID());
					kickPresence.setTo(removeJID);

					// 通知被踢者，
					router.route(kickPresence);

					// 通知讨论组其它成员
					send(kickPresence, room);

				} else {
					throw new UnauthorizedException();
				}
			} finally {
				roomLock.unlock();
			}
		}
	}
	
	/**
	 * 
	 * 查询用户所在的群组列表<br>
	 * add comment by buya
	 *
	 * @param packet
	 * @return
	 */
	private void queryUserRooms(IQ replyIQ, IQ packet) throws ForbiddenException{		
		String namespace = packet.getChildElement().getNamespaceURI();		
		RedisMUCUser user = allUsersCache.get(packet.getFrom().toBareJID());
		
		if (user == null) {
			throw new ForbiddenException();
		}
		
		Element qElement = replyIQ.setChildElement("query",namespace);
		JID[] rooms = user.getRoomJids();
		Element itemElement = null;
		RedisMUCRoom room = null;
		
		for (JID jid : rooms) {
			room = allRoomsCache.get(jid.toBareJID());
			RedisMUCMember member = room.getMember(packet.getFrom().asBareJID());
			if (room != null && null!=member) {
				itemElement = qElement.addElement("item",namespace);
				itemElement.addAttribute("jid", jid.toString());
				itemElement.addAttribute("name", null==room.getSubject()?"":room.getSubject());
				itemElement.addAttribute("newAPNSMsg", null==member.getNewAPNSMsg()?"0":member.getNewAPNSMsg());
				itemElement.addAttribute("top", null==member.getTop()?"0":member.getTop());
				itemElement.addAttribute("saveAsRoster", null==member.getSaveAsRoster()?"0":member.getTop());
			}
		}
	}
	
	/**
	 * 
	 * 查询群组成员
	 *
	 * @param item
	 * @param reply
	 * @param members
	 * @param owner
	 * @throws ForbiddenException
	 * @throws ConflictException
	 * @throws NotAllowedException
	 * @throws CannotBeInvitedException
	 */
	private void queryRoomMembers(IQ reply, IQ packet, RedisMUCRoom room) throws ForbiddenException,
			ConflictException, NotAllowedException, CannotBeInvitedException {

		Element queryElement = packet.getChildElement();
		String namespace = queryElement.getNamespaceURI();
		Element item = queryElement.element("item");

		Element result = reply.setChildElement("query", namespace);
		
		if ("member".equals(item.attributeValue("affiliation"))) {
			Element memItemElement;
			for (RedisMUCMember mem : room.getMembers()) {
				memItemElement = result.addElement("item", namespace);   //XXX is namespace required here?
				if (room.getOwner().getNode().equals(mem.getJid().getNode())) {
					memItemElement.addAttribute("affiliation", "owner");
				} else {
					memItemElement.addAttribute("affiliation", "member");
				}
				memItemElement.addAttribute("jid", mem.getJid().toBareJID());
				memItemElement.addAttribute("role", "member");
				memItemElement.addAttribute("nick", null==mem.getNickName()?"":mem.getNickName());
			}
			RedisMUCMember member = room.getMember(packet.getFrom().asBareJID());
			Element x = reply.getElement().addElement("x", "http://jabber.org/protocol/muc#userSetting");
			
			Element setting = x.addElement("info");
			setting.addElement("name").addText(null==room.getSubject()?"":room.getSubject());
			setting.addElement("newAPNSMsg").addText(null==member.getNewAPNSMsg()?"0":member.getNewAPNSMsg());
			setting.addElement("top").addText(null==member.getTop()?"0":member.getTop());
			setting.addElement("saveAsRoster").addText(null==member.getSaveAsRoster()?"0":member.getTop());
		}else{
			throw new ForbiddenException();
		}
	}


	public boolean isHidden() {
		return false;
	}

	public void setDescription(String description) {

	}
}
